package com.charlessong.hcnetsdk.demo.alarm;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * @author charles
 * @Date 2021/11/29
 */
@Slf4j
@UtilityClass
public class HCNetSDKUtils {
    static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE;

    static int lUserID = -1;//用户句柄
    static int m_lSetCardCfgHandle = -1; //下发卡长连接句柄
    static int m_lSetFaceCfgHandle = -1; //下发人脸长连接句柄

    static int dwState = -1; //下发卡数据状态
    static int dwFaceState = -1; //下发人脸数据状态


    static int lHandle = -1; //下发人脸数据状态
    static int lAlarmHandle = -1; //下发人脸数据状态

    static int iCharEncodeType = 0;//设备字符集

    /**
     * 登录
     *
     * @return
     */
    public static int login(String deviceIp, short devicePort, String userName, String password) {
        //必须先初始化资源
        hCNetSDK.NET_DVR_Init();

        //注册之前先注销已注册的用户,预览情况下不可注销
        if (lUserID > -1) {
            //先注销
            hCNetSDK.NET_DVR_Logout(lUserID);
            lUserID = -1;
        }

        HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();//设备登录信息
        HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();//设备信息

        m_strLoginInfo.sDeviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];
        System.arraycopy(deviceIp.getBytes(), 0, m_strLoginInfo.sDeviceAddress, 0, deviceIp.length());

        m_strLoginInfo.sUserName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];
        System.arraycopy(userName.getBytes(), 0, m_strLoginInfo.sUserName, 0, userName.length());

        m_strLoginInfo.sPassword = new byte[HCNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN];
        System.arraycopy(password.getBytes(), 0, m_strLoginInfo.sPassword, 0, password.length());

        m_strLoginInfo.wPort = devicePort;

        m_strLoginInfo.bUseAsynLogin = false; //是否异步登录：0- 否，1- 是

        m_strLoginInfo.write();

        lUserID = hCNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo);

        if (lUserID > -1) {
            log.error("====> 设备注册成功 userId:{}", lUserID);
        } else {
            log.error("====> 设备注册失败,error:{} ", hCNetSDK.NET_DVR_GetLastError());
        }
        return lUserID;
    }

    public static int setUpAlarm() {
        //开始布防
        HCNetSDK.NET_DVR_SETUPALARM_PARAM m_strAlarmInfo = new HCNetSDK.NET_DVR_SETUPALARM_PARAM();
        m_strAlarmInfo.dwSize = m_strAlarmInfo.size();
        m_strAlarmInfo.byLevel = 0;//智能交通布防优先级：0- 一等级（高），1- 二等级（中），2- 三等级（低）
        m_strAlarmInfo.byAlarmInfoType = 1;//智能交通报警信息上传类型：0- 老报警信息（NET_DVR_PLATE_RESULT），1- 新报警信息(NET_ITS_PLATE_RESULT)
        m_strAlarmInfo.byDeployType = 1; //布防类型(仅针对门禁主机、人证设备)：0-客户端布防(会断网续传)，1-实时布防(只上传实时数据)
        m_strAlarmInfo.write();
        int lAlarmHandle = hCNetSDK.NET_DVR_SetupAlarmChan_V41(lUserID, m_strAlarmInfo);
        if (lAlarmHandle == -1) {
            log.error("====>布防失败，错误号:{} ", hCNetSDK.NET_DVR_GetLastError());
        } else {
            log.error("====>布防成功");
        }
        return lAlarmHandle;
    }

    public void logout() {
        hCNetSDK.NET_DVR_Logout(lUserID);
        //注销之后，释放资源
        hCNetSDK.NET_DVR_Cleanup();
    }


    //时间格式转换
    public static HCNetSDK.NET_DVR_TIME getHkTime(Date time) {
        HCNetSDK.NET_DVR_TIME structTime = new HCNetSDK.NET_DVR_TIME();
        String str = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(time);
        String[] times = str.split("-");
        structTime.dwYear = Integer.parseInt(times[0]);
        structTime.dwMonth = Integer.parseInt(times[1]);
        structTime.dwDay = Integer.parseInt(times[2]);
        structTime.dwHour = Integer.parseInt(times[3]);
        structTime.dwMinute = Integer.parseInt(times[4]);
        structTime.dwSecond = Integer.parseInt(times[5]);
        return structTime;
    }

    /**
     * 获取卡列表
     */
    public JSONArray listCards() throws Exception {
        JSONArray jsonArray = new JSONArray();
        HCNetSDK.NET_DVR_CARD_COND struCardCond = new HCNetSDK.NET_DVR_CARD_COND();
        struCardCond.read();
        struCardCond.dwSize = struCardCond.size();
        struCardCond.dwCardNum = 0xffffffff; // 查询所有
        struCardCond.write();
        Pointer ptrStruCond = struCardCond.getPointer();

        int m_lSetCardCfgHandle = hCNetSDK.NET_DVR_StartRemoteConfig(lUserID, HCNetSDK.NET_DVR_GET_CARD, ptrStruCond,
                struCardCond.size(), null, null);
        if (m_lSetCardCfgHandle == -1) {
            throw new Exception("建立下发卡长连接失败，错误码为" + hCNetSDK.NET_DVR_GetLastError());
        }

        HCNetSDK.NET_DVR_CARD_RECORD struCardRecord = new HCNetSDK.NET_DVR_CARD_RECORD();
        struCardRecord.read();
        struCardRecord.dwSize = struCardRecord.size();
        struCardRecord.write();

        while (true) {
            int dwState = hCNetSDK.NET_DVR_GetNextRemoteConfig(m_lSetCardCfgHandle, struCardRecord.getPointer(),
                    struCardRecord.size());
            struCardRecord.read();
            if (dwState == -1) {
                log.error("NET_DVR_SendWithRecvRemoteConfig接口调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEEDWAIT) {
                log.info("配置等待");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException ignored) {
                }
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED) {
                log.warn("获取卡参数失败");
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_EXCEPTION) {
                log.warn("获取卡参数异常");
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("employeeNo", struCardRecord.dwEmployeeNo);
                jsonObject.put("cardNo", hcBytesToString(struCardRecord.byCardNo));
                jsonObject.put("name", hcBytesToString(struCardRecord.byName));
                jsonObject.put("start", formatTime(struCardRecord.struValid.struBeginTime));
                jsonObject.put("end", formatTime(struCardRecord.struValid.struEndTime));
//                jsonObject.put("type", struCardRecord.byCardType);
//                jsonObject.put("userType", struCardRecord.byUserType);
                jsonArray.add(jsonObject);
                log.info("获取卡参数成功, 卡号: " + hcBytesToString(struCardRecord.byCardNo) + ", 卡类型："
                        + struCardRecord.byCardType + ", 姓名：" + new String(struCardRecord.byName, "GBK").trim());
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
                log.info("获取卡参数完成");
                break;
            }
        }

        if (!hCNetSDK.NET_DVR_StopRemoteConfig(m_lSetCardCfgHandle)) {
            log.error("NET_DVR_StopRemoteConfig接口调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
        } else {
            log.info("NET_DVR_StopRemoteConfig接口成功");
        }
        return jsonArray;

    }


    /**
     * 获取卡信息
     */
    public JSONObject getCardInfo(String cardNo) throws Exception {
        JSONObject jsonObject = null;
        HCNetSDK.NET_DVR_CARD_COND struCardCond = new HCNetSDK.NET_DVR_CARD_COND();
        struCardCond.read();
        struCardCond.dwSize = struCardCond.size();
        struCardCond.dwCardNum = 1; //查询一个卡参数
        struCardCond.write();
        Pointer ptrStruCond = struCardCond.getPointer();

        int lHandler = hCNetSDK.NET_DVR_StartRemoteConfig(lUserID, HCNetSDK.NET_DVR_GET_CARD, ptrStruCond, struCardCond.size(), null, null);
        if (lHandler == -1) {
            throw new Exception("建立查询卡参数长连接失败，错误码为" + hCNetSDK.NET_DVR_GetLastError());
        }

        //查找指定卡号的参数，需要下发查找的卡号条件
        HCNetSDK.NET_DVR_CARD_SEND_DATA struCardNo = new HCNetSDK.NET_DVR_CARD_SEND_DATA();
        struCardNo.read();
        struCardNo.dwSize = struCardNo.size();

        for (int i = 0; i < HCNetSDK.ACS_CARD_NO_LEN; i++) {
            struCardNo.byCardNo[i] = 0;
        }
        System.arraycopy(cardNo.getBytes(), 0, struCardNo.byCardNo, 0, cardNo.length());
        struCardNo.write();
        //用户记录
        HCNetSDK.NET_DVR_CARD_RECORD struCardRecord = new HCNetSDK.NET_DVR_CARD_RECORD();
        struCardRecord.read();

        IntByReference pInt = new IntByReference(0);

        while (true) {
            int dwState = hCNetSDK.NET_DVR_SendWithRecvRemoteConfig(lHandler, struCardNo.getPointer(), struCardNo.size(),
                    struCardRecord.getPointer(), struCardRecord.size(), pInt);
            struCardRecord.read();
            if (dwState == -1) {
                System.out.println("NET_DVR_SendWithRecvRemoteConfig查询卡参数调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEEDWAIT) {
                System.out.println("配置等待");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException ignored) {
                }
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED) {
                log.error("获取卡参数失败, 卡号: " + cardNo);
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_EXCEPTION) {
                log.error("获取卡参数异常, 卡号: " + cardNo);
                break;
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS) {
                jsonObject = new JSONObject();
                jsonObject.put("name", new String(struCardRecord.byName).trim());
                jsonObject.put("type", struCardRecord.byCardType);
                jsonObject.put("cardNo", new String(struCardRecord.byCardNo).trim());
                jsonObject.put("no", struCardRecord.dwEmployeeNo);
                jsonObject.put("start", formatTime(struCardRecord.struValid.struBeginTime));
                jsonObject.put("end", formatTime(struCardRecord.struValid.struEndTime));
            } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
                log.info("获取卡参数完成");
                break;
            }
        }

        if (!hCNetSDK.NET_DVR_StopRemoteConfig(lHandler)) {
            log.warn("NET_DVR_StopRemoteConfig接口调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
        }
        return jsonObject;
    }

    public static final int BYTE_ARRAY_LEN = 1024;

    /**
     * 查询人员信息(列表)
     *
     * @param start       分页用，起始位置
     * @param limit       分页用，限制每页多少个用户
     * @param employeeNos 如果参数为空，就查询全部人员信息
     */
    public JSONObject searchUserInfo(int start, int limit, String... employeeNos) throws Exception {
        JSONObject jsonObject = new JSONObject();

        HCNetSDK.BYTE_ARRAY ptrByteArray = new HCNetSDK.BYTE_ARRAY(BYTE_ARRAY_LEN);    //数组
        String strInBuffer = "POST /ISAPI/AccessControl/UserInfo/Search?format=json";
        System.arraycopy(strInBuffer.getBytes(), 0, ptrByteArray.byValue, 0, strInBuffer.length());//字符串拷贝到数组中
        ptrByteArray.write();

        int lHandler = hCNetSDK.NET_DVR_StartRemoteConfig(lUserID, 2550/*NET_DVR_JSON_CONFIG*/, ptrByteArray.getPointer(), strInBuffer.length(), null, null);
        if (lHandler < 0) {
            throw new Exception("SearchUserInfo NET_DVR_StartRemoteConfig 失败,错误码为" + hCNetSDK.NET_DVR_GetLastError());
        } else {
            //组装查询的JSON报文，这边查询的是所有的卡
            JSONObject jsonSearchCond = new JSONObject();

            //如果需要查询指定的工号人员信息
            if (employeeNos.length > 0) {
                JSONArray employeeArray = new JSONArray();
                for (String employeeNo : employeeNos) {
                    employeeArray.add((new JSONObject()).put("employeeNo", employeeNo));
                }
                jsonSearchCond.put("EmployeeNoList", employeeArray);
            }

            jsonSearchCond.put("searchID", "123e4567-e89b-12d3-a456-426655440000");
            jsonSearchCond.put("searchResultPosition", start);
            jsonSearchCond.put("maxResults", limit);

            String strInbuff = new JSONObject().put("UserInfoSearchCond", jsonSearchCond).toString();
//            log.info("查询人员的json报文:" + strInbuff);

            //把string传递到Byte数组中，后续用.getPointer()方法传入指针地址中。
            HCNetSDK.BYTE_ARRAY ptrInbuff = new HCNetSDK.BYTE_ARRAY(strInbuff.length());
            System.arraycopy(strInbuff.getBytes(), 0, ptrInbuff.byValue, 0, strInbuff.length());
            ptrInbuff.write();

            //定义接收结果的结构体
            HCNetSDK.BYTE_ARRAY ptrOutuff = new HCNetSDK.BYTE_ARRAY(10 * 1024);

            IntByReference pInt = new IntByReference(0);

            while (true) {
                int dwState = hCNetSDK.NET_DVR_SendWithRecvRemoteConfig(lHandler, ptrInbuff.getPointer(), strInbuff.length(), ptrOutuff.getPointer(), 10 * 1024, pInt);
                System.out.println(dwState);
                if (dwState == -1) {
                    log.error("NET_DVR_SendWithRecvRemoteConfig接口调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
                    break;
                } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_NEEDWAIT) {
                    log.info("配置等待");
                    try {
                        Thread.sleep(10);
                    } catch (Exception ignored) {
                    }
                } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FAILED) {
                    log.warn("查询人员失败");
                    break;
                } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_EXCEPTION) {
                    log.warn("查询人员异常");
                    break;
                } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_SUCCESS) {
                    ptrOutuff.read();

                    jsonObject = JSONObject.parseObject(new String(ptrOutuff.byValue).trim()).getJSONObject("UserInfoSearch");
                    break;
                } else if (dwState == HCNetSDK.NET_SDK_CONFIG_STATUS_FINISH) {
                    log.info("获取人员完成");
                    break;
                }
            }

            if (!hCNetSDK.NET_DVR_StopRemoteConfig(lHandler)) {
                log.error("NET_DVR_StopRemoteConfig接口调用失败，错误码：" + hCNetSDK.NET_DVR_GetLastError());
            }
            return jsonObject;
        }

    }


    /**
     * 去掉空白这种特殊字符
     */
    public static String hcBytesToString(byte[] bytes) {
        return new String(bytes).replace("\u0000", "");
    }

    public static String formatTime(HCNetSDK.NET_DVR_TIME_EX ex) {
        if (ex == null) {
            return "";
        }
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, ex.wYear);
        calendar.set(Calendar.MONTH, ex.byMonth - 1);
        calendar.set(Calendar.DAY_OF_MONTH, ex.byDay);
        calendar.set(Calendar.HOUR_OF_DAY, ex.byHour);
        calendar.set(Calendar.MINUTE, ex.byMinute);
        calendar.set(Calendar.SECOND, ex.bySecond);
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(calendar.getTime());
    }
}
